PowerShell [Universal] Adventures

Table of Contents
Intro #
It has been a while since my last post, so let’s start with something new-ish!
Normally, I am not a huge fan of using PowerShell for GUI building, but I recently came across PowerShell Universal — out of necessity for a project — and I thought I’d write up my mostly positive experience with it. (Disclaimer: I am not affiliated with them!)
Installation #
I am going to largely skip this, as the documentation is quite clear on the process. In a nutshell, I installed the community edition (and the matching extension) to work on this project. It was mostly a trivial affair.
I also created a dedicated service account for this in Active Directory (not for the service; that runs as the local system—more for AD-related tasks).
A simple AD tool #
In this blog (which might or might not grow into a series), I built a tool to manage simple tasks of an AD administrator, such as managing user accounts (one could say, this is something trivial, but I thought I learn the tool with something familiar!)
Full disclosure: this post largely follows JackedProgrammer’s guide, so feel free to follow that if you prefer.
Define endpoints #
Anyway, once I got everything installed, I went ahead and created a number of API endpoints:


Write API code #
I created these endpoints via the GUI, so the code here focuses only on the actions we want to perform in Active Directory:
- Getting details of a user account:
Import-Module ActiveDirectory
$ADCredential = $secret:ADCredentialPRD
$ADServer = "< your domain controller >"
# $username = # provided by the :username
$splat = @{
Identity = $username
Server = $ADServer
Credential = $ADCredential
}
$user = Get-ADUser -Properties * @splat
$result = [PSCustomObject]@{
Name = $user.Name
UserName = $user.SamAccountName
UserPrincipalName = $user.UserPrincipalName
EmployeeID = $user.EmployeeID
Title = $user.Title
Enabled = $user.Enabled
PasswordExpired = $user.PasswordExpired
LockedOut = $user.LockedOut
LastBadPasswordAttempt = $user.LastBadPasswordAttempt
}
$result
- getting groups of a user account:
Import-Module ActiveDirectory
$ADCredential = $secret:ADCredentialPRD
$ADServer = "< your domain controller >"
# $UserName = # provided by the :UserName
$splat = @{
Identity = $UserName
Server = $ADServer
Credential = $ADCredential
}
$Result = @()
$groups = Get-ADPrincipalGroupMembership @splat
foreach ($group in $groups) {
$GroupDetails = Get-ADGroup -Identity $group -Properties * -Server $ADServer
$Entry = [PSCustomObject]@{
Name = $group.Name
GroupCategory = $group.GroupCategory.ToString()
GroupScope = $GroupDetails.GroupScope.ToString()
Description = $GroupDetails.Description
}
$Result += $Entry
}
$Result
- unlock a user account:
Import-Module ActiveDirectory
$ADCredential = $secret:ADCredentialPRD
$ADServer = "< your domain controller >"
# $UserName = # provided by the :UserName
$splat = @{
Identity = $UserName
Server = $ADServer
Credential = $ADCredential
}
Unlock-ADAccount -Confirm:$false @splat
$User = Get-ADUser -Properties lockedout @splat
$result = [PSCustomObject]@{
UserName = $UserName
LockedOut = $User.LockedOut
}
return $result
- reset password of the user account:
Import-Module ActiveDirectory
$ADCredential = $secret:ADCredentialPRD
$ADServer = "< your domain controller >"
# $UserName = # provided by the :UserName
$PasswordLength = 12 # default pw lenght, if no resultant password policy exists
$splat = @{
Identity = $UserName
Server = $ADServer
Credential = $ADCredential
}
$PasswordPolicy = $null
$PasswordPolicy = Get-ADUserResultantPasswordPolicy @splat
# overide password length if the user has a policy for this
if ($PasswordPolicy) {
$PasswordLength = $PasswordPolicy.MinPasswordLength
}
$UpperCaseSet = (65..90) | ForEach-Object { [char]$_ }
$LowerCaseSet = (97..122) | ForEach-Object { [char]$_ }
$NumericSet = (48..57) | ForEach-Object { [char]$_ }
$SpecialSet = (33, 35, 36, 37, 38, 42, 63) | ForEach-Object { [char]$_ }
$CharSet = $UpperCaseSet + $LowerCaseSet + $NumericSet + $SpecialSet
$PasswordPlainText = -join (Get-Random -Count $PasswordLength -InputObject $CharSet)
$Password = $PasswordPlainText | ConvertTo-SecureString -AsPlainText -Force
$Password
Set-ADAccountPassword -NewPassword $Password -Reset @splat
Set-ADUser -ChangePasswordAtLogon $true -PasswordNeverExpires $false @splat
$result = [PSCustomObject]@{
UserName = $UserName
NewPassword = $PasswordPlainText
}
return $result
- enable the user account:
Import-Module ActiveDirectory
$ADCredential = $secret:ADCredentialPRD
$ADServer = "< your domain controller >"
# $UserName = # provided by the :UserName
$splat = @{
Identity = $UserName
Server = $ADServer
Credential = $ADCredential
}
Set-ADUser -Enabled $true -Confirm:$false @splat
$User = Get-ADUser -Properties Enabled @splat
$result = [PSCustomObject]@{
UserName = $UserName
Enabled = $User.Enabled
}
return $result
- or disable the user account:
Import-Module ActiveDirectory
$ADCredential = $secret:ADCredentialPRD
$ADServer = "< your domain controller >"
# $UserName = # provided by the :UserName
$splat = @{
Identity = $UserName
Server = $ADServer
Credential = $ADCredential
}
Set-ADUser -Enabled $false -Confirm:$false @splat
$User = Get-ADUser -Properties Enabled @splat
$result = [PSCustomObject]@{
UserName = $UserName
Enabled = $User.Enabled
}
return $result
Admin Page #
Now that I had the actions for the API-s configured, I went away and tested each using PostMan (but really Invoke-Webrequest
or curl
could have done it) and ensured the actions work.
Then I have added the code for a page, to display information and interact with AD using the API-s.

This simple page:
- allows you to input the name of a user (
samAccountName
needed!) - provided the user exists, it then
- populates some basic information on the account (1)
- pulls in the group memberships of the account (2)
- and displays buttons for the basic acctions (2)
Important to note, the action buttons dynamicaly change:
- if the account is locked -
Unlock Account
will be click-able - if the account is disabled -
Enable Account
will become click-able - similarly - as you can see on my very much enabled account -
Disable Account
is active if the account is enabled Reset Password
is always active, and selecting it displays in a modal (pop-up) the new random password generated
Before I forget: the page is actually two pages:
- the main page:
New-UDApp -Title "Powershell Universal" -Pages @(
Get-UDPage -Name "ADUserTool"
)
- then the tool page:
New-UDGrid -Container -Content {
## Grid for search fields [grid is 12 unit wide!]
New-UDGrid -Item -ExtraSmallSize 2 -Content {
New-UDForm -SubmitText "Search" -Content {
New-UDTextbox -Id "UserName" -Label "Username"
} -OnSubmit {
try {
## Collect user details via API calls
$UserName = $EventData.Username
$UserDetails = Invoke-RestMethod -Uri "http://localhost:5000/ad/getuser/$username" -Method Get
$UserGroups = Invoke-RestMethod -Uri "http://localhost:5000/ad/getusergroups/$username" -Method Get
## Display user groups
Set-UDElement -Id "Groups" -Properties @{
data = $UserGroups
}
## Display basic user info
$Properties = ($UserDetails | Get-Member | Where-Object MemberType -eq NoteProperty).Name
foreach ($property in $Properties) {
Set-UDElement -Id "UserDetails$Property" -Properties @{
content = "$property : $($UserDetails.$property)"
}
}
## Set button states based on user state
Set-UDElement -Id "ResetPassword" -Properties @{
disabled = $false
}
if ($UserDetails.LockedOut -eq $true) {
Set-UDElement -Id "UnlockAccount" -Properties @{
disabled = $false
}
}
else {
Set-UDElement -Id "UnlockAccount" -Properties @{
disable = $true
}
}
if ($UserDetails.enabled -eq $true) {
Set-UDElement -Id "DisableAccount" -Properties @{
disabled = $false
}
Set-UDElement -Id "EnableAccount" -Properties @{
disabled = $true
}
}
else {
Set-UDElement -Id "DisableAccount" -Properties @{
disabled = $true
}
Set-UDElement -Id "EnableAccount" -Properties @{
disabled = $false
}
}
}
catch {
## Error for non-existing user
Show-UDModal -Content {
New-UDAlert -Severity Error -Text "UserName not found - $($_)"
}
## Reset user details for non-existing user
Set-UDElement -Id "Groups" -Properties @{
data = ""
}
Set-UDElement -Id "UserDetailsName" -Properties @{
content = " "
}
Set-UDElement -Id "UserDetailsUserName" -Properties @{
content = " "
}
Set-UDElement -Id "UserDetailsUserPrincipalName" -Properties @{
content = " "
}
Set-UDElement -Id "UserDetailsEmployeeID" -Properties @{
content = " "
}
Set-UDElement -Id "UserDetailsTitle" -Properties @{
content = " "
}
Set-UDElement -Id "UserDetailsEnabled" -Properties @{
content = " "
}
Set-UDElement -Id "UserDetailsPasswordExpired" -Properties @{
content = " "
}
Set-UDElement -Id "UserDetailsLockedOut" -Properties @{
content = " "
}
Set-UDElement -Id "UserDetailsLastBadPasswordAttempt" -Properties @{
content = " "
}
## Update buton states for non-existing user
Set-UDElement -Id "ResetPassword" -Properties @{
disabled = $true
}
Set-UDElement -Id "UnlockAccount" -Properties @{
disabled = $true
}
Set-UDElement -Id "DisableAccount" -Properties @{
disabled = $true
}
Set-UDElement -Id "EnableAccount" -Properties @{
disabled = $true
}
}
}
}
## Grid for buttons
New-UDGrid -Item -ExtraSmallSize 10 -Content {
New-UDParagraph -Content {
New-UDButton -Id "UnlockAccount" -Text "Unlock Account" -Disabled -OnClick {
## Select user account
$UserName = (Get-UDElement -Id "UserDetailsUserName").content.split(':')[1].trim()
## Button action (process user)
Invoke-RestMethod -Uri "http://localhost:5000/ad/unlockaccount/$UserName" -Method Post
## Display updated details
$UserDetails = Invoke-RestMethod -Uri "http://localhost:5000/ad/getuser/$UserName" -Method Get
$Properties = ($UserDetails | Get-Member | Where-Object MemberType -eq NoteProperty).Name
foreach ($property in $Properties) {
Set-UDElement -Id "UserDetails$Property" -Properties @{
content = "$property : $($UserDetails.$property)"
}
}
## Update button state post-action
Set-UDElement -Id "UnlockAccount" -Properties @{
disabled = $true
}
}
New-UDButton -Id "EnableAccount" -Text "Enable Account" -Disabled -OnClick {
## Select user account
$UserName = (Get-UDElement -Id "UserDetailsUserName").content.split(':')[1].trim()
## Button action (process user)
Invoke-RestMethod -Uri "http://localhost:5000/ad/enableaccount/$UserName" -Method Post
## Display updated details
$UserDetails = Invoke-RestMethod -Uri "http://localhost:5000/ad/getuser/$UserName" -Method Get
$Properties = ($UserDetails | Get-Member | Where-Object MemberType -eq NoteProperty).Name
foreach ($property in $Properties) {
Set-UDElement -Id "UserDetails$Property" -Properties @{
content = "$property : $($UserDetails.$property)"
}
}
## Update button states post-action
Set-UDElement -Id "EnableAccount" -Properties @{
disabled = $true
}
Set-UDElement -Id "DisableAccount" -Properties @{
disabled = $false
}
}
New-UDButton -Id "DisableAccount" -Text "Disable Account" -Disabled -OnClick {
## Select user account
$UserName = (Get-UDElement -Id "UserDetailsUserName").content.split(':')[1].trim()
## Button action (process user)
Invoke-RestMethod -Uri "http://localhost:5000/ad/disableaccount/$UserName" -Method Post
## Display updated details
$UserDetails = Invoke-RestMethod -Uri "http://localhost:5000/ad/getuser/$UserName" -Method Get
$Properties = ($UserDetails | Get-Member | Where-Object MemberType -eq NoteProperty).Name
foreach ($property in $Properties) {
Set-UDElement -Id "UserDetails$Property" -Properties @{
content = "$property : $($UserDetails.$property)"
}
}
## Update button states post-action
Set-UDElement -Id "EnableAccount" -Properties @{
disabled = $false
}
Set-UDElement -Id "DisableAccount" -Properties @{
disabled = $true
}
}
New-UDButton -Id "ResetPassword" -Text "Reset Password" -Disabled -OnClick {
## Select user account
$UserName = (Get-UDElement -Id "UserDetailsUserName").content.split(':')[1].trim()
## Button action (process user)
$NewPassword = Invoke-RestMethod -Uri "http://localhost:5000/ad/resetpassword/$UserName" -Method Post
# Display pop-up with password
Show-UDModal -Content {
New-UDAlert -Severity Info -Text "Password for $($NewPassword.UserName) has been reset to: $($NewPassword.NewPassword)"
}
}
}
}
## Grid for User Details
New-UDGrid -Item -ExtraSmallSize 5 -Content {
New-UDHeading -Content { "User details" } -Size 2 -Color White
New-UDElement -Id "UserDetails" -Tag "ul" -Content {
New-UDElement -Id "UserDetailsName" -Tag "li" -Content {}
New-UDElement -Id "UserDetailsUserName" -Tag "li" -Content {}
New-UDElement -Id "UserDetailsUserPrincipalName" -Tag "li" -Content {}
New-UDElement -Id "UserDetailsEmployeeID" -Tag "li" -Content {}
New-UDElement -Id "UserDetailsTitle" -Tag "li" -Content {}
New-UDElement -Id "UserDetailsEnabled" -Tag "li" -Content {}
New-UDElement -Id "UserDetailsPasswordExpired" -Tag "li" -Content {}
New-UDElement -Id "UserDetailsLockedOut" -Tag "li" -Content {}
New-UDElement -Id "UserDetailsLastBadPasswordAttempt" -Tag "li" -Content {}
}
}
## Grid for Group Membership
New-UDGrid -Item -ExtraSmallSize 5 -Content {
New-UDHeading -Content {
"Group Membership"
} -Size 2 -Color White
New-UDTable -Id "Groups" -Columns @(
New-UDTableColumn -Property "Name" -Title "Name"
New-UDTableColumn -Property "GroupCategory" -Title "Group Category"
New-UDTableColumn -Property "GroupScope" -Title "Group Scope"
New-UDTableColumn -Property "Description" -Title "Description"
)
}
}
As I put quite extensive comments along the blocks of the code, I let you explore it.